
#if defined(WIN32)
#include <winsock2.h>
typedef SOCKET socket_t;
typedef int socklen_t;
#else
#include <sys/types.h>
#include <sys/socket.h>
//#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
typedef int socket_t;
#define INVALID_SOCKET  -1
#define SOCKET_ERROR -1
#endif

#if defined(WIN32)
#include <windows.h>
#else
#include <unistd.h>
#endif

#include <errno.h>
#include <signal.h>
#include <string.h>
#include <cassert>
#include <iostream>
using namespace std;

static bool finished = false;

static void signalHandler(int signo)
{
    std::cerr << "Shutting down" << endl;
    finished = true;
}

void msleep(int millsecs = 1000)
{
#if defined(WIN32)
    ::Sleep(millsecs);
#else
    usleep(1000 * millsecs);
#endif
}

class SockInitializor
{
public:
    bool Useable() const { return mUseable; }
    SockInitializor()
    {
#if defined(WIN32)
        mUseable = false;

        WORD wVersionRequested = MAKEWORD(2, 2);
        WSADATA wsaData;
        int err;

        err = WSAStartup(wVersionRequested, &wsaData);
        if (err != 0)
        {
            // could not find a usable WinSock DLL
            //cerr << "Could not load winsock" << endl;
            //resip_assert(0); // is this is failing, try a different version that 2.2, 1.0 or later will likely work
            //exit(1);
        }

        /* Confirm that the WinSock DLL supports 2.2.*/
        /* Note that if the DLL supports versions greater    */
        /* than 2.2 in addition to 2.2, it will still return */
        /* 2.2 in wVersion since that is the version we      */
        /* requested.                                        */

        if (LOBYTE(wsaData.wVersion) != 2 ||
            HIBYTE(wsaData.wVersion) != 2)
        {
            /* Tell the user that we could not find a usable */
            /* WinSock DLL.                                  */
            WSACleanup();
            //cerr << "Bad winsock verion" << endl;
            // TODO !cj! - add error message logging
            //resip_assert(0); // if this is failing, try a different version that 2.2, 1.0 or later will likely work
            //exit(1);
        }
        mUseable = true;
#else
        mUseable = true;
#endif
    }
    ~SockInitializor()
    {
#if defined(WIN32)
        if (mUseable) WSACleanup();
#endif
    }
private:
    bool mUseable;
};

class Socket
{
public:
    enum TransportType {UDP, TCP};
    Socket(TransportType type, bool createWhenInit = true) : mType(type), mHandle(-1), mPort(0) {
        memset(&mSockV4, 0, sizeof(mSockV4));
        if (createWhenInit) create(type);
    }
    ~Socket() {
        if (isValid())
        {
#if defined(WIN32)
            closesocket(mHandle);
#else
            close(mHandle);
#endif
        }
    }
    bool create(TransportType type) {
        if (isValid()) return true;
        switch (type)
        {
        case Socket::UDP:
            mHandle = ::socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/);
            break;
        case Socket::TCP:
            mHandle = ::socket(PF_INET, SOCK_STREAM, 0/*IPPROTO_TCP*/);
            break;
        default:
            assert(0);
            break;
        }
        
        return isValid();
    }
    bool isValid() const { return mHandle!= INVALID_SOCKET; }

    bool bind(const char* ip = NULL, int port = 0) {
        if (ip) mIpv4 = ip;
        mPort = port;
        mSockV4.sin_family = AF_INET;
        mSockV4.sin_port = htons(port);
        if (!ip) mSockV4.sin_addr.s_addr = htonl(INADDR_ANY);
        else mSockV4.sin_addr.s_addr = inet_addr(ip);
        if (SOCKET_ERROR  == ::bind(mHandle, &mSockAddr, sizeof(mSockAddr)))
        {
            //bind fail
#if defined(WIN32)
            mErrno = ::WSAGetLastError();
#else
            mErrno = errno;
#endif
            return false;
        }

        // If we bound to port 0, then query OS for assigned port number
        if (port == 0)
        {
            socklen_t len = sizeof(mSockAddr);
            if (SOCKET_ERROR == ::getsockname(mHandle, &mSockAddr, &len))
            {
                //get bind port fail
                cerr << "Could not get bind port" << endl;
#if defined(WIN32)
                mErrno = ::WSAGetLastError();
#else
                mErrno = errno;
#endif
            }
            else
            {
                mPort = ntohs(mSockV4.sin_port);
            }
        }
//         if (!makeNonBlocking())
//         {
//             cerr << "Could not make socket non-blocking on port:" << getBindPort() << endl;
//         }

        return true;
    }

    bool listen()
    {
        return SOCKET_ERROR != ::listen(mHandle, 64);
    }

    bool makeNonBlocking() {
#if defined(WIN32)
        unsigned long noBlock = 1;
        int errNoBlock = ::ioctlsocket(mHandle, FIONBIO, &noBlock);
        if (errNoBlock != 0)
        {
            return false;
        }
#else
        int flags = fcntl(mHandle, F_GETFL, 0);
        int errNoBlock = fcntl(mHandle, F_SETFL, flags | O_NONBLOCK);
        if (errNoBlock != 0) // !cj! I may have messed up this line
        {
            return false;
        }
#endif
        return true;
    }

    bool makeBlocking(Socket fd)
    {
#if defined(WIN32)
        unsigned long noBlock = 0;
        int errNoBlock = ioctlsocket(mHandle, FIONBIO, &noBlock);
        if (errNoBlock != 0)
        {
            return false;
        }
#else
        int flags = fcntl(mHandle, F_GETFL, 0);
        int errNoBlock = fcntl(mHandle, F_SETFL, flags & ~O_NONBLOCK);
        if (errNoBlock != 0) // !cj! I may have messed up this line
        {
            return false;
        }
#endif
        return true;
    }

    TransportType getTransportType() const { return mType; }
    const char* getBindIpv4() const { return mIpv4.c_str(); }
    int getBindPort() const { return mPort; }
    int getErrno() const { return mErrno; }

private:
    //Socket(const Socket&);
    //Socket operator=(const Socket&);
protected:
    int setopt(int level, int optname, void* optval, int optlen) {
#if defined(WIN32)
        return ::setsockopt(mHandle, level, optname, (const char*)optval, optlen);
#else
        return ::setsockopt(mHandle, level, optname, optval, optlen);
#endif
    }

    TransportType mType;
    socket_t mHandle;
    union {
        sockaddr mSockAddr;
        sockaddr_in mSockV4;
    };
    string mIpv4;
    int mPort;
    int mErrno;
};



int main()
{
    if (signal(SIGINT, signalHandler) == SIG_ERR)
    {
        cerr << "Couldn't install signal handler for SIGINT" << endl;
        return 0;
    }

    if (signal(SIGTERM, signalHandler) == SIG_ERR)
    {
        cerr << "Couldn't install signal handler for SIGTERM" << endl;
        return 0;
    }

    SockInitializor net;
    if (!net.Useable())
    {
        cout << "sock unavliable!" << endl;
        return 0;
    }

    Socket s(Socket::TCP);
    if (!s.isValid())
    {
        cout << "failed to create socket with errno:" << s.getErrno() << endl;
        return 0;
    }

    if (!s.bind())
    {
        cout << "failed to bind socket to all interfaces and system specified port with errno:" << s.getErrno() << endl;
        return 0;
    }

    cout << "bind to local port:" << s.getBindPort() << endl;

    if (!s.makeNonBlocking())
    {
        cerr << "Could not make socket non-blocking on port:" << s.getBindPort() << endl;
        return 0;
    }

    if (s.getTransportType() == Socket::TCP)
    {
        if (!s.listen())
        {
            cerr << "Could not listen" << endl;
            return 0;
        }
    }

    while (!finished);

    return 0;
}



